home *** CD-ROM | disk | FTP | other *** search
- /*
- Test code for the SlidingTiles Programmer's Challenge
- */
-
- /* undef ANIMATE to delete animation of your solution */
- #define ANIMATE 1
- //#undef ANIMATE
-
- /* define MOVE_USER_TILE to also move the user's tile in the MakeMove callback */
- #undef MOVE_USER_TILE
- //#define MOVE_USER_TILE
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include "Solve.h"
- #include "TileMain.h"
-
- #define kNormalSize 20
- #define kOffset 20
- #define kMinWindowSize 300
- #define RECTWIDTH(r) ((r).right-(r).left)
- #define RECTHEIGHT(r) ((r).bottom-(r).top)
-
- static long gNumRows,gNumCols; /* initialized by test code */
- static long gEmptyRow,gEmptyCol; /* initialized by test code */
- static long *gTiles; /* initialized by test code */
-
- static FontInfo gFontInfo;
- static WindowPtr gWind;
- static Rect gRect;
- static short gAnimateSpeed,gTileSize,gFontSize,gOffset;
-
- #define TileValue(tiles,row,col,numCols) *(tiles+(row)*numCols+(col))
- #define OutOfRange(val,num) (((val)<0) || ((val)>=(num)))
-
- /*
- Calculate the distance of the row/col tile from its correct position.
- */
- static long TileDist(long *tiles,long row,long col,long numCols)
- {
- long row0,col0,val;
- val = TileValue(tiles,row,col,numCols);
- row0 = val / numCols;
- col0 = val - row0*numCols;
- return abs(row-row0) + abs(col-col0);
- }
-
- /*
- Calculate a score for this position, the sum of all tile distances
- from their correct position.
- */
- static long CalcScore(long *tiles,long numRows,long numCols)
- {
- long row,col,dist,cumDist=0;
- for (row=0; row<numRows; ++row) {
- for (col=0; col<numCols; ++col) {
- if ((row==0) && (col==0)) continue;
- dist = TileDist(tiles,row,col,numCols);
- cumDist += dist;
- }
- }
- return cumDist;
- }
-
- /*
- Draw a status string in the animation window.
- */
- static void MyDrawString(StringPtr str)
- {
- Rect r;
-
- SetRect(&r,5,5,160,15);
- EraseRect(&r);
- MoveTo(r.left,r.bottom);
- TextSize(9);
- DrawString(str);
- TextSize(gFontSize);
- }
-
- /*
- Draw the position score in the animation window.
- */
- static void DrawScore(long dist)
- {
- Str255 str;
- sprintf((char *)str,"Distance from solution: %ld",dist);
- c2pstr((char *)str);
- MyDrawString(str);
- }
-
- /*
- Draw one tile.
- */
- static void DrawOneTile(long *tiles,long theRow,long theCol)
- {
- long val,wid;
- Rect r;
- Str255 valStr;
-
- SetRect(&r,gOffset,gOffset,gOffset+gTileSize,gOffset+gTileSize);
- val = TileValue(tiles,theRow,theCol,gNumCols);
- OffsetRect(&r,gTileSize*theCol,gTileSize*theRow);
- if (val != 0) {
- PenSize(2,2);
- FrameRect(&r);
- PenNormal();
- NumToString(val,valStr);
- InsetRect(&r, 1, 1);
- EraseRect(&r);
- wid = StringWidth(valStr);
- MoveTo(gOffset + theCol*gTileSize+gTileSize/2-wid/2,gOffset + theRow*gTileSize+(gTileSize+gFontInfo.ascent)/2);
- DrawString(valStr);
- } else {
- PaintRect(&r);
- }
- }
-
- /*
- Draw all tiles
- */
- static void DrawTiles(WindowPtr wind)
- {
- GrafPtr savePort;
- long row,col;
- Rect r;
- GetPort(&savePort);
- SetPort(wind);
- EraseRect(&((GrafPtr)wind)->portRect);
- SetRect(&r,gOffset-1,gOffset-1,gOffset+1+gNumCols*gTileSize,gOffset+1+gNumRows*gTileSize);
- FrameRect(&r);
- for (row=0; row<gNumRows; ++row) {
- for (col=0; col<gNumCols; ++col) {
- DrawOneTile(gTiles,row,col);
- }
- }
- SetPort(savePort);
- }
-
- /*
- Initialize all tiles in their correct positions. Allocate storage.
- */
- static long *InitTiles(long numRows, long numCols)
- {
- int row,col;
- long index = 0;
- long *gtiles = (long *)NewPtr(numRows*numCols*sizeof(long));
- long *tiles = gtiles;
- if (nil != gtiles) {
- for (row=0; row<numRows; ++row) {
- for (col=0; col<numCols; ++col)
- *tiles++ = index++;
- }
- }
- return gtiles;
- }
-
- /*
- Dispose of tile storage.
- */
- static void DisposeTiles(long *tiles)
- {
- DisposePtr((Ptr)tiles);
- }
-
- #define kMoveLeftRight 0x01
- #define kMoveUpLeft 0x02
-
- #define Swap(tiles,a,b) \
- { long temp; \
- temp=tiles[a];tiles[a]=tiles[b];tiles[b]=temp; \
- }
-
- static void ExpressScramble(long *tiles,long numRows,long numCols) {
- //Scrambles a sliding tile puzzle of any size quickly.
- //(Courtesy of Ernst Munter)
- long i,b,numSwaps=0;
- i=numRows*numCols;
- do {
- --i;
- b=1+(rand()%i);
- if (b!=i) {
- Swap(tiles,i,b);
- numSwaps++;
- }
- } while (i>3);
- if (numSwaps&1) Swap(tiles,1,2);
-
- //at this point the puzzle is scrambled, with every
- //possible legal (solvable) state reached with equal
- //probability, EXCEPT the blank tile is still at gTiles[0].
- //No unsolvable states can be reached with this algorithm.
-
- //To move the blank tile inside the puzzle, use MakeMove
- //just a few times, say 1 or 2 times the number of tiles.
-
- }
-
- /*
- Scramble the puzzle by moving the blank tile a bunch of times.
- */
- static void ScrambleTiles(long numIters, MoveProc Move)
- {
- long dir,i,rowToMove,colToMove;
- static long prevDir = -1;
- Boolean legal;
- MoveTo(160,15);
- DrawString("\p SCRAMBLING ...");
- for (i=0; i<numIters; ++i) {
- do {
- rowToMove = gEmptyRow;
- colToMove = gEmptyCol;
- dir = Random()&0x03;
- /* dir == 0x00 move Up tile down into blank
- == kMoveLeftRight move Left tile right into blank
- == kMoveUpLeft move Down tile up into blank
- == kMoveLeftRight+kMoveDownRight move Right tile left into blank */
- if (dir & kMoveLeftRight) /* move Left Or Right */ {
- if (dir & kMoveUpLeft) /* try to move tile left into empty square */ {
- if (gEmptyCol < gNumCols-1) ++colToMove;
- else {dir ^= kMoveUpLeft; --colToMove;}
- } else { /* try to move tile right into empty square */
- if (gEmptyCol > 0) --colToMove;
- else {dir ^= kMoveUpLeft; ++colToMove;}
- }
- } else { /* move Up or Down */
- if (dir & kMoveUpLeft) /* try to move tile up into empty square */ {
- if (gEmptyRow < gNumRows-1) ++rowToMove;
- else {dir ^= kMoveUpLeft; --rowToMove;}
- } else { /* try to move tile down into empty square */
- if (gEmptyRow > 0) --rowToMove;
- else {dir ^= kMoveUpLeft; ++rowToMove;}
- }
- }
- } while ( (dir^prevDir) == kMoveUpLeft ); /* avoid sequential moves that cancel */
- prevDir = dir;
- legal = (*Move)(rowToMove,colToMove);
-
- }
- }
-
- /*
- Determine if the puzzle is in the pristine state.
- */
- static Boolean SolutionIsCorrect(long *tiles,long numRows,long numCols)
- {
- long numTiles = numRows*numCols;
- while (0 <= --numTiles)
- if (numTiles != *(tiles+numTiles)) return false;
- return true;
- }
-
- /*
- Update tile window.
- */
- void DoUpdate(WindowPtr wind)
- {
- BeginUpdate(wind);
- #ifdef ANIMATE
- DrawTiles(wind);
- #endif
- EndUpdate(wind);
- }
-
- /*
- Callback routine to move tile.
- */
- static Boolean MakeMove(long tileToMoveRow,long tileToMoveCol)
- {
- long diff;
- if (OutOfRange(tileToMoveRow,gNumRows)) return false;
- if (OutOfRange(tileToMoveCol,gNumCols)) return false;
- if (tileToMoveRow == gEmptyRow) {
- diff = tileToMoveCol - gEmptyCol;
- } else if (tileToMoveCol == gEmptyCol) {
- diff = tileToMoveRow - gEmptyRow;
- } else {
- return false;
- }
- if ((diff != -1) && (diff != 1)) return false;
- TileValue(gTiles,gEmptyRow,gEmptyCol,gNumCols) =
- TileValue(gTiles,tileToMoveRow,tileToMoveCol,gNumCols);
- #ifdef ANIMATE
- if (gAnimateSpeed>0) {
- DrawOneTile(gTiles,gEmptyRow,gEmptyCol);
- }
- #endif
- gEmptyRow = tileToMoveRow;
- gEmptyCol = tileToMoveCol;
- TileValue(gTiles,gEmptyRow,gEmptyCol,gNumCols) = 0;
- #ifdef ANIMATE
- if (gAnimateSpeed>0) {
- DrawOneTile(gTiles,gEmptyRow,gEmptyCol);
- Delay(gAnimateSpeed,&diff);
- DrawScore(CalcScore(gTiles,gNumRows,gNumCols));
- }
- #endif
- return true;
- }
-
- /*
- Execute one test case.
- */
- void RunOneCase(WindowPtr wind,long numRows,long numCols)
- {
- GDHandle gd;
- EventRecord ev;
- long *userTiles;
- short width,height,mBarHeight,hPos,vPos;
-
- /* Initialize */
- gWind = wind;
- gNumRows = numRows;
- gNumCols = numCols;
- gEmptyRow = gEmptyCol = 0;
- gTiles = InitTiles(gNumRows,gNumCols);
- if (gTiles==nil) DebugStr("\p problem allocating tile memory");
- gFontSize = 9;
- if (numRows>numCols) gTileSize = (kMinWindowSize-2*kOffset)/numRows;
- else gTileSize = (kMinWindowSize-2*kOffset)/numCols;
- gFontSize = gTileSize/2;
- if (gTileSize<kNormalSize) {
- gTileSize = kNormalSize;
- gFontSize = 9;
- }
- TextSize(gFontSize);
- GetFontInfo(&gFontInfo);
-
- /* Adjust animation window size */
- width = 2*kOffset + gTileSize*numCols;
- height = 2*kOffset + gTileSize*numRows;
- gOffset = kOffset;
-
- if (width<kMinWindowSize) {
- width = kMinWindowSize;
- gOffset = (kMinWindowSize - gTileSize*numCols)/2;
- }
- if (height<kMinWindowSize) {
- height = kMinWindowSize;
- gOffset = (kMinWindowSize - gTileSize*numRows)/2;
- }
- width = 2*gOffset + gTileSize*numCols;
- height = 2*gOffset + gTileSize*numRows;
-
- /* Adjust animation window position */
- mBarHeight = LMGetMBarHeight();
- gd = GetMainDevice();
- gRect = (**gd).gdRect;
- hPos = (RECTWIDTH(gRect)-width)/2;
- vPos = mBarHeight+(RECTHEIGHT(gRect)-height-mBarHeight)/2;
- if (hPos<0) hPos = 0;
- if (vPos<mBarHeight) vPos=mBarHeight;
- MoveWindow(gWind,hPos,vPos,false);
- SizeWindow(gWind,width, height, true);
- ShowWindow(gWind);
- SetPort(gWind);
-
- /* Initialize tiles */
- gAnimateSpeed=0;
- ExpressScramble(gTiles,numRows,numCols);
- ScrambleTiles(numRows*numCols*40L,MakeMove);
- DrawScore(CalcScore(gTiles,gNumRows,gNumCols));
- DrawTiles(gWind);
- gAnimateSpeed=4;
-
- userTiles = (long *)NewPtr(gNumRows*gNumCols*sizeof(long));
- if (userTiles==nil) DebugStr("\p problem allocating user tile memory");
- memcpy(userTiles,gTiles,gNumRows*gNumCols*sizeof(long));
-
- /*
- CALL USER SOLUTION
- */
- SolveTiles(userTiles,gNumRows,gNumCols,MakeMove);
-
-
- /* Determine if solution is correct */
- if (SolutionIsCorrect(gTiles,gNumRows,gNumCols)) {
- MyDrawString("\p Solution is correct, hit a key to continue.");
- } else {
- MyDrawString("\p Solution is NOT correct, hit a key to continue.");
- }
- /* Wait for acknowledgement */
- SysBeep(1);
- do { ;
- } while (!WaitNextEvent(keyDownMask,&ev,0,nil));
-
- /* Clean up and leave */
- HideWindow(gWind);
- DisposeTiles(userTiles);
- DisposeTiles(gTiles);
- }
-